module NavigatorHelper
  # Devuelve un symbol con el nombre del modelo relacionado con el controller actual.
  def get_model
    controller.model_name
  end
  
  # Devuelve una instancia con la lista de elementos leidas por el controller actual.
  def get_list
    nombre = get_model.to_s.pluralize
    instance_variable_get("@#{nombre}")
  end
  
  def get_controller_name
    controller.class.to_s.underscore.gsub(/_controller$/,"")
  end
    
  def form_parameters(*args)
    options = args.last.is_a?(Hash) ? args.pop : {}
    name   = args.shift || get_model
    object = args.shift || @current
    [name, object, options]
  end
  
  def grid_parameters(*args)
    options = args.last.is_a?(Hash) ? args.pop : {}
    name    = args.shift || get_model
    object  = args.shift || get_list
    [name, object, options]
  end
  
  def tabs
    rtn = ''
    if controller.class.left_tabs || controller.class.right_tabs
      rtn << left_tabs(*controller.class.left_tabs)
      rtn << right_tabs(*controller.class.right_tabs)
    elsif @nav_ltabs || @nav_rtabs
      rtn << nav_left_tabs
      rtn << nav_right_tabs
    end
    rtn
  end
  
  def left_tabs(*resources)
    lis = []
    resources.each do |tab|
      lis << tab_to_li(tab)
    end
    !lis.empty? ? content_tag(:ul, lis.join("\n"), :id => 'tab') : ''
  end
  deprecate :left_tabs
  
  def right_tabs(*resources)
    lis = []
    style  = 'float: right; '
    margin = 'margin-right: 30px;'
    resources.each do |tab|
      lis << tab_to_li(tab, :style => style + margin)
      margin = ''
    end
    !lis.empty? ? content_tag(:ul, lis.join("\n"), :id => 'tab') : ''
  end
  deprecate :right_tabs
  
  def tab_to_li(tab, options = {})
    url = nil
    if tab.is_a?(Array)
      resource = tab[0].to_s
      text = tab[1]
      url = tab[2]
    elsif tab.is_a?(Hash)
      resource = tab.keys.first.to_s
      text     = resource.humanize
      accion   = tab.values.first.to_s
      url = {:controller => resource, :action => accion}
    else
      resource = tab.to_s
      text = resource.humanize
      url = {:controller => resource, :action => 'index'}
    end
    klass = controller.controller_name == resource ? 'current' : nil
    content_tag(:li, link_to(text, url), options.merge(:class => klass))
  end
  deprecate :tab_to_li
  
  def nav_left_tabs
    return '' if @nav_ltabs.empty?
    lis = []
    @nav_ltabs.each do |tab|
      lis << nav_tab_to_li(tab)
    end
    content_tag(:ul, lis.join("\n"), :id => 'tab')
  end
  
  def nav_right_tabs
    return '' if @nav_rtabs.empty?
    lis = []
    style  = 'float: right; '
    margin = 'margin-right: 30px;'
    @nav_rtabs.each do |tab|
      lis << nav_tab_to_li(tab, :style => style + margin)
      margin = ''
    end
    content_tag(:ul, lis.join("\n"), :id => 'tab')
  end
  
  def nav_tab_to_li(tab, li_options = {})
    text = tab.shift
    url = tab.shift
    klass = controller.controller_name == url[:controller].to_s ? 'current' : nil
    content_tag(:li, link_to(text, url), li_options.merge(:class => klass))
  end

  def extra_stylesheets
    case @aplicacion_css
      when Array
        @aplicacion_css.inject(''){|rtn, css| rtn << stylesheet_link_tag(css)}
      when String, Symbol
        stylesheet_link_tag(@aplicacion_css)
      when Hash
        rtn = ''
        @aplicacion_css.each do |media, name|
          rtn << stylesheet_link_tag(name, :media => media)
        end
        rtn
    end
  end

  # Devuelve una lista de los filtros definidos para el navegador.
  # Solo mostrarlos si se está en una acción que muestre la lista.
  def filters
    return '' if controller.action_name != 'index'
    content_tag(:div, regular_filters + combo_filters, :id => 'filters')
  end
  
  def regular_filters
    return '' if controller.class.filters.empty?
    lis = []
    tidy_filters.each do |filter|
      current_filter = session[:"#{controller.model_name}_filter"] || :all
      cls = (current_filter == filter.name) ? 'current' : nil
      link = link_to_remote(filter.label, toggle_spinner(:filters_spinner, :url => {:action => 'index', :filter => filter.name, :page => 1}, :method => :get))
      lis << content_tag(:li, link, :class => cls)
    end
    content = content_tag(:h2, "Filtrar por" + spinner(:filters_spinner)) +
      content_tag(:ul, lis.join("\n"), :class => 'filters')
  end
  
  def tidy_filters
    cc = controller.class
    filters = cc.filters.dup
    if !cc.options[:remove_filters]
      if !cc.get_filter(:all) 
        filter = ActsAsDynamic::ClassMethods::Filter.new
        filter.name = :all
        filter.label = 'Mostrar todo'
        filters.unshift filter
      end
    else
      filters.delete_if {|f| f.name == :all}
    end
    filters
  end
  
  def combo_filters
    (controller.class.combo_filters || []).map {|f| combo_filter(f)}.join("\n")
  end
  
  def combo_filter(filter)
    rtn = []
    spinner_id = "#{filter.name}_spinner"
    rtn << content_tag(:h2, (filter.options[:label] || filter.name.to_s.humanize))
    rtn << select_tag(filter.name, options_for_combo_filter(filter))
    rtn << observe_field(filter.name, toggle_spinner(spinner_id, :url => {:action => 'index'}, :with => filter.name.to_s, :method => :get))
    rtn << spinner(spinner_id)
    rtn.join("\n")
  end
  
  def options_for_combo_filter(filter)
    list = filter.block.call
    list.unshift [(filter.options[:label_all] || '(Mostrar todos)'), 0]
    selected = nil
    list.each do |name, id|
      selected = [name, id] if id.to_s == session[:"#{filter.name}_combo_filter"]
    end
    options_for_select(list, selected)
  end
  
  # Devuelve un string con las acciones del elemento.
  def show_actions(*args)
    options = args.last.is_a?(Hash) ? args.pop : {}
    element = (args.shift || @current)
    actions = []
    unless options[:edit] == false
      url = my_url_for(controller.controller_name, 'edit', element, controller.class.options[:parent])
      actions << link_to_remote('Editar', toggle_spinner(:edit_spinner, :url => url, :method => :get))
      actions << spinner(:id => :edit_spinner)
    end
    actions << action_links_array(element, :remote => false)
    content_tag :p, actions.join(' '), :class => 'show_actions'
  end
  
  # Devuelve un array con los links a las acciones del elemento.
  def action_links_array(element, options = {})
    options[:remote] = true if options[:remote].nil?
    links = []
    if options[:model]
      controller_name = options[:controller_name] || (options[:model].to_s.pluralize + '_controller')
      controller_class = Object::const_get(controller_name.to_s.classify)
      actions = controller_class.actions
    else
      actions = controller.class.actions
    end
    return links if actions.empty?
    actions.each do |name, label, action_options, block|
      action_options = action_options.dup
      if url = action_options.delete(:url)
        url.merge!(:id => element)
      elsif block
        url = block.call(element)
      else
        url = {:action => name, :id => element}
      end
      if url
        html_options = action_options.delete(:html)
        if options[:remote] && action_options.delete(:remote)
          links << link_to_remote(label, action_options.merge(:url => url), 
          html_options)
        else
          links << link_to(label, action_options.merge(url), 
          html_options)
        end
      end
    end
    links
  end

  def show_flash(flash_hash = flash)
    aux =  '<b class="rtop"><b class="r1"></b> <b class="r2"></b> <b class="r3"></b> <b class="r4"></b></b>'
    aux << flash_hash.inject('') do |rtn, (key, message)|
      id = "flash_#{key}"
      rtn << content_tag(:div, content_tag(:span, message), :id => id, :class => 'flash', :onclick => "hide_element(this);")
    end    
    aux << '<b class="rbottom"><b class="r4"></b> <b class="r3"></b> <b class="r2"></b> <b class="r1"></b></b>'
  end

  def error_messages_for(*params)
    options = params.last.is_a?(Hash) ? params.pop.symbolize_keys : {}
    if params.empty?
      objects = [instance_variable_get("@#{controller.model_name}")]
    else
      objects = params.collect {|object_name| instance_variable_get("@#{object_name}") }.compact
    end
    count   = objects.inject(0) {|sum, object| sum + object.errors.count }
    unless count.zero?
      html = {}
      [:id, :class].each do |key|
        if options.include?(key)
          value = options[key]
          html[key] = value unless value.blank?
        else
          html[key] = 'errorExplanation'
        end
      end
      prh = count > 1 ? 'impidieron' : 'impidió'
      header_message = "#{pluralize(count, 'error')} #{prh} que #{(options[:object_name] || params.first).to_s.gsub('_', ' ')} sea guardada."
      error_messages = objects.map {|object| object.errors.full_messages.map {|msg| content_tag(:li, msg) } }
      content_tag(:div, content_tag(options[:header_tag] || :h2, header_message) <<
      content_tag(:p,   'Hubo problemas con los siquientes campos:') <<
      content_tag(:ul,   error_messages), html )
    else
     ''
    end
  end
  
  def spinner(*args)
    options = args.last.is_a?(Hash) ? args.pop : {}
    id = args.shift
    image_tag('spinner.gif', {:style => 'display:none', :id => (id || 'spinner'), :class => 'spinner'}.merge(options))
  end
  
  def search
    return '' if controller.class.search_fields.empty? || controller.action_name != 'index'
    input = text_field_tag(:search, params[:search] || '', :id => :search, :class => :entrada, :autocomplete => 'off')
    observer = observe_field(:search, toggle_spinner(:search_spinner, :url => {:action => 'index', :page => 1}, :with => 'search', :frequency => 0.4, :method => :get))
    input + spinner(:search_spinner) + observer
  end
  
  def toggle_spinner(spinner = 'spinner', options = {})
    err_msg = show_flash(:error => 'Ha ocurrido un error en la aplicación.')
    options.merge :loading => "Element.show('#{spinner}'); #{options[:loading]}; ", 
      :complete => "Element.hide('#{spinner}'); #{options[:complete]}; ", 
      :failure => "Element.update('flash_area', '#{err_msg}'); #{options[:failure]}; "
  end
  
  def my_submit_to_remote(text, options = {})
    button_id = options[:id] || "button_#{text.object_id}"
    spinner_id = "#{button_id}_spinner"    
    loading_js = update_page do |page|
      page[spinner_id].show
      page[button_id].disabled = true
    end
    complete_js = <<-end_js
      if ($('#{spinner_id}')) {
        Element.hide('#{spinner_id}');
        $('#{button_id}').disabled = false;
      }
    end_js
    options = options.merge :html => {:id => button_id}, :loading => loading_js, :complete => complete_js
    submit_to_remote(button_id, text, options) + 
    spinner(:id => spinner_id)
  end
  
  def render_list(controller, list, options = {})
    model_name = options[:model] ? options[:model] : controller.to_s.singularize
    @template.instance_variable_set("@#{model_name.to_s.pluralize}", list)
    content_tag(:div, render(:partial => "#{controller}/list", :locals => {:options => options}), :id => "#{model_name}_grid")
  end
  
  def hide_flash(key)
    page.delay(5) do
      page["flash_#{key}"].hide
    end
  end
  
  def tag_for(name, *options, &proc)
    content = options.shift unless options.first.is_a?(Hash)
    content ||= nil
    options = options.shift
    if block_given?
      texto = "<#{name}#{tag_options(options.stringify_keys) if options}>"
      texto << yield 
      texto << "</#{name}>" 
    elsif content.nil?
      "<#{name}#{tag_options(options.stringify_keys) if options} />"
    else
      content_tag(name, content, options)
    end
  end  

  def my_url_for(*args)
    controller.my_url_for(*args)
  end
  
  def my_url_for_hash(*args)
    controller.my_url_for_hash(*args)
  end

  def modo(*args, &block)
    controller.modo(*args, &block)
  end

end

ActionView::Base.send :include, NavigatorHelper
